`include "defines.v"
module crossbar # (
    parameter RW_DATA_WIDTH     = 64,
    parameter RW_ADDR_WIDTH     = 64,
    parameter AXI_DATA_WIDTH    = 64,
    parameter AXI_ADDR_WIDTH    = 32,
    parameter AXI_ID_WIDTH      = 4,
    parameter AXI_USER_WIDTH    = 1
)(
    input clock,
    input reset,
    
    //from if stage
    input if_valid_i,
    input [AXI_DATA_WIDTH-1:0]if_addr_i,
    input [1:0]if_size_i,
    //to if stage
    //output if_ready_o,
    output reg [RW_DATA_WIDTH-1:0]if_data_read_o,
    output reg [1:0]if_resp_o,

    //from mem stage
    input mem_valid_i,
    input [AXI_DATA_WIDTH-1:0]mem_addr_i,
    input [1:0]mem_size_i,
    input mem_load_en_i,
    input mem_save_en_i,
    input [RW_DATA_WIDTH-1:0]mem_data_write_i,

    //to mem stage
    //output mem_ready_o,
    output reg [RW_DATA_WIDTH-1:0]mem_data_read_o,
    output reg [1:0]mem_resp_o,

    //to axi_rw
    output reg rw_valid_o,
    output reg rw_req_o,
    output wire [RW_DATA_WIDTH-1:0]data_write_o,
    output reg [AXI_DATA_WIDTH-1:0]rw_addr_o,
    output reg [1:0]rw_size_o,
        
    //from axi_rw
    input rw_ready_i,
    input [RW_DATA_WIDTH-1:0]data_read_i,
    input [1:0]rw_resp_i,

    //to pipeline
    output reg axi_done
);

    reg [2:0]current_state;
    wire handshake;
    assign handshake = rw_valid_o && rw_ready_i;
    assign data_write_o = mem_data_write_i;

    //state machine
    parameter [2:0] IDLE_STATE = 3'b000, SINGLE_TASK_STATE = 3'b001, TASK_ONE_STATE = 3'b010, TASK_TWO_STATE = 3'b011, PIPELINE_STATE = 3'b100;

    always@( posedge clock) 
    begin
        if( reset == 1'b1 ) begin
            current_state <= IDLE_STATE;
            rw_valid_o <= 1'b0;
            rw_req_o <= 1'b0;
            rw_addr_o <= {RW_DATA_WIDTH{1'b0}};
            rw_size_o <= 2'b11;
            axi_done <= 1'b0;
            if_data_read_o<=0;
            if_resp_o<=0;
            mem_data_read_o<=0;
            mem_resp_o<=0;
        end
        else begin
            //DEFAULT:
            //rw_valid_o <= 1'b0;
            //rw_req_o <= 1'b0;
            //rw_addr_o <= {RW_DATA_WIDTH{1'b0}};
            //rw_size_o <= 2'b11;
            //axi_done <= 1'b0;

            case( current_state )
            IDLE_STATE: begin 

                if (((if_valid_i == 1'b1) && (mem_valid_i == 1'b0)) || ((if_valid_i == 1'b0) && (mem_valid_i == 1'b1))) begin
                    if (if_valid_i == 1'b1) begin   //task from if_stage
                        rw_valid_o <= if_valid_i;
                        rw_req_o <= 1'b0;
                        rw_addr_o <= if_addr_i;
                        rw_size_o <= if_size_i;
                    end
                    else if ((mem_valid_i == 1'b1)) begin //if the task is from mem_stage
                        rw_valid_o <= mem_valid_i;
                        rw_addr_o <= mem_addr_i;
                        rw_size_o <= mem_size_i;
                        if (mem_save_en_i) begin
                            rw_req_o <= 1'b1;
                        end
                        else if (mem_load_en_i) begin
                            rw_req_o <= 1'b0;
                        end
                 
                    end
                    current_state <= SINGLE_TASK_STATE;
                end
                else if ((if_valid_i == 1'b1) && (mem_valid_i == 1'b1)) begin
                    rw_valid_o <= if_valid_i;
                    rw_req_o <= 1'b0;
                    rw_addr_o <= if_addr_i;
                    rw_size_o <= if_size_i;
                    current_state <= TASK_ONE_STATE;
                end
            end

            SINGLE_TASK_STATE: begin
                if (if_valid_i == 1'b1) begin   //task from if_stage
                    if (handshake==1'b1) begin
                        axi_done <= 1'b1;
                        rw_valid_o <= 0;
                        if_data_read_o <= data_read_i;
                        if_resp_o <= rw_resp_i;
                        current_state <= PIPELINE_STATE;
                    end
                end
                else if ((mem_valid_i == 1'b1)) begin //if the task is from mem_stage
                    if (handshake==1'b1) begin
                        axi_done <= 1'b1;
                        rw_valid_o <= 0;
                        mem_data_read_o <= data_read_i;
                        mem_resp_o <= rw_resp_i;
                        current_state <= PIPELINE_STATE;
                    end                   
                end

            end
            
            TASK_ONE_STATE: begin
                if (handshake==1'b1) begin
                    if_data_read_o <= data_read_i;
                    if_resp_o <= rw_resp_i;
                    rw_valid_o <= mem_valid_i;
                    rw_addr_o <= mem_addr_i;
                    rw_size_o <= mem_size_i;
                    if (mem_save_en_i) begin
                        rw_req_o <= 1'b1;
                    end
                    else if (mem_load_en_i) begin
                        rw_req_o <= 1'b0;
                    end
                    current_state <= TASK_TWO_STATE;
                end               
            end

            TASK_TWO_STATE: begin
                if (handshake==1'b1) begin
                    axi_done <= 1'b1;
                    rw_valid_o <= 0;
                    mem_data_read_o <= data_read_i;
                    mem_resp_o <= rw_resp_i;
                    current_state <= PIPELINE_STATE;
                end                
            end

            PIPELINE_STATE: begin
                axi_done <= 1'b0;
                rw_valid_o <= 0;
                if_data_read_o <= `ZERO_WORD;
                mem_data_read_o <= `ZERO_WORD;
                if_resp_o <= 2'b00;
                mem_resp_o <= 2'b00;
                current_state <= IDLE_STATE;
            end

            default: begin
                rw_valid_o <= 0;
                current_state <= IDLE_STATE;
            end
            endcase


        end
    end


endmodule